﻿using Graphviz4Net.Graphs;
using Graphviz4Net.WPF.Example;
using LogAnalyzer.Model;
using LogAnalyzer.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Windows;

namespace LogAnalyzer.ViewModel
{
    public class JoinGraphViewModel : IGraphViewModel
    {
        public Graph<GraphComponent> Graph { get; private set; }

        private Node theNode;

        public string Header { get; set; }

        public bool MnuRoute { get { return false; } }

        public bool MnuJoin { get { return false; } }

        public bool MnuAddJoin { get { return true; } }

        public bool ShowJoin { get; set; }

        public bool ShowJoinResponse { get; set; }

        public bool ShowJoinDone { get; set; }

        public Dictionary<string, string> ColorMap { get; set;}

        private List<LogMessage> bigList;

        List<string> colors = new List<string> { "red", "green", "blue", "yellow", "black" };

        public JoinGraphViewModel(Node node)
        {
            theNode = node;
            ShowJoin = true;
            ShowJoinDone = false;
            ShowJoinResponse = false;

            Graph = new Graph<GraphComponent>();
            bigList = new List<LogMessage>();
            ColorMap = new Dictionary<string, string>();

            Refresh();
        }

        public void AddNodeJoin(Node node)
        {
            string addr =  "akka://System/user/" + (int.Parse(node.Name) - 1);
            var joinMsg = App.Log.FirstOrDefault(x => x.what.type == Util.MessageType.Join && x.from == node.address && x.to.address == addr);
            if (joinMsg == null)
            {
                var n = this.Graph.Vertices.FirstOrDefault(x => x.Id.Equals(node.address));
                if (n == null)
                {
                    var c = node.Clone();
                    Graph.AddVertex(c);
                    c.showTable += node_showTable;
                }
            }
            else
            {
                var joinList = App.Log.Where(x => x.what.content != null ? x.what.content.id == joinMsg.what.content.id : false).OrderBy(x => x.time).ToList();
                joinList.ForEach(x => bigList.Add(x));
            }
        }

        void node_showTable(object sender, EventArgs e)
        {
            var node = sender as Node;
            var routingInfoView = new RoutingInfoView();

            var m = App.Log.First(x => x.to.nodeId == node.nodeId && x.what.type == Util.MessageType.StartSending);
            node.leaf = m.what.state.leaf;
            node.table = m.what.state.table;

            var routingInfoViewModel = new RoutingInfoViewModel(node.leaf, node.tableData, node);
            routingInfoView.DataContext = routingInfoViewModel;

            Window window = new Window
            {
                Title = "Node Routing Info " + node.nodeId,
                Content = routingInfoView
            };

            window.Show();
        }

        public void LoadJoinGraph(List<LogMessage> joinList)
        {
            for (int i = 0; i < joinList.Count; i++)
            {
                var msg = joinList[i];
                var id = msg.what.content == null ? "0" : msg.what.content.id;
                if (msg.what.type.Equals("Join") && ShowJoin)
                {
                    var from = this.Graph.Vertices.FirstOrDefault(x => x.Id.Equals(msg.from));
                    if (from == null)
                    {
                        from = App.Nodes.First(x => x.address == msg.from).Clone();
                        ((Node)from).showTable += node_showTable;
                        Graph.AddVertex(from);
                    }

                    var to = this.Graph.Vertices.FirstOrDefault(x => x.Equals(msg.to));
                    if (to == null)
                    {
                        to = App.Nodes.First(x => x.address == msg.to.address).Clone();
                        ((Node)to).showTable += node_showTable;
                        Graph.AddVertex(to);
                    }

                    this.Graph.AddEdge(new Edge<GraphComponent>(from, to, new Arrow()) { Label = "Join(" + (Graph.Edges.Count() + 1) + ")", Color = ColorMap[id] });
                }
                else if (msg.what.type.Equals("JoinResponse") && ShowJoinResponse )
                {
                    var from = this.Graph.Vertices.FirstOrDefault(x => x.Id.Equals(msg.from));
                    if (from == null)
                    {
                        from = App.Nodes.First(x => x.address == msg.from).Clone();
                        ((Node)from).showTable += node_showTable;
                        Graph.AddVertex(from);
                    }

                    var to = this.Graph.Vertices.FirstOrDefault(x => x.Equals(msg.to));
                    if (to == null)
                    {
                        to = App.Nodes.First(x => x.address == msg.to.address).Clone();
                        ((Node)to).showTable += node_showTable;
                        Graph.AddVertex(to);
                    }

                    this.Graph.AddEdge(new Edge<GraphComponent>(from, to, new Arrow()) { Label = "JoinResponse(" + (Graph.Edges.Count() + 1) + ")", Color = ColorMap[id] });
                }
                else if (msg.what.type.Equals("JoinDone") && ShowJoinDone)
                {
                    var from = this.Graph.Vertices.FirstOrDefault(x => x.Id.Equals(msg.from));
                    if (from == null)
                    {
                        from = App.Nodes.First(x => x.address == msg.from).Clone();
                        ((Node)from).showTable += node_showTable;
                        Graph.AddVertex(from);
                    }

                    var to = this.Graph.Vertices.FirstOrDefault(x => x.Equals(msg.to));
                    if (to == null)
                    {
                        to = App.Nodes.First(x => x.address == msg.to.address).Clone();
                        ((Node)to).showTable += node_showTable;
                        Graph.AddVertex(to);
                    }

                    var edge = new Edge<GraphComponent>(from, to, new Arrow()) { Label = "JoinDone(" + (Graph.Edges.Count() + 1) + ")", Color = ColorMap[id] };

                    this.Graph.AddEdge(edge);
                }
            }
        }

        public void Refresh()
        {
            while (Graph.Vertices.Count() > 0)
            {
                Graph.RemoveVertexWithEdges(Graph.Vertices.First());
            }

            bigList.Clear();
            ColorMap.Clear();

            var list = new List<Node>();
            list.Add(theNode);

            foreach (var m in App.Log.Where(x => x.what.type == Util.MessageType.StartSending))
            {
                if (m.what.state.leaf.smaller.Exists(x => x.nodeId == theNode.nodeId))
                {
                    list.Add(m.to);
                }
                else if (m.what.state.leaf.larger.Exists(x => x.nodeId == theNode.nodeId))
                {
                    list.Add(m.to);
                }
                else
                {
                    foreach (var row in m.what.state.table)
                    {
                        if (row.Exists(x => x.nodeId == theNode.nodeId))
                        {
                            list.Add(m.to);
                            break;
                        }
                    }
                }
            }

            foreach (var n in list)
            {
                AddNodeJoin(n);
            }

            var ids = (from n in bigList select new { id = (n.what.content == null ? "0" : n.what.content.id) }).Distinct();
            int color = 0;
            foreach (var id in ids)
            {
                if (id.id != "0")
                {
                    ColorMap.Add(id.id, colors[color]);
                    color++;
                    if (color >= colors.Count)
                    {
                        color = 0;
                    }
                }
            }

            bigList = bigList.OrderBy(x => x.time).ToList();
            LoadJoinGraph(bigList);
        }
    }
}
